import java.math.BigInteger;
import java.util.Random;
import java.util.Scanner;

/**
 * @author guanhao 观浩
 * @version 1.0.0.0
 * @createTime 2023/7/3 12:32 PM
 * @company Michale Squirrel
 * @link
 * @description
 */
public class TestRSA {

    //RSA算法
        @SuppressWarnings("resource")
        public static void main(String[] args) {
            TestRSA rsa=new TestRSA();
            BigInteger[] primeNumber = rsa.getPrimeNumber();
            //step1:产生两个大素数
            BigInteger p=primeNumber[0];
            BigInteger q=primeNumber[1];
            System.out.println("1、B产生的两个大素数是(保密)：p="+p+" q="+q);
            //step2.1:计算n
            BigInteger n=p.multiply(q);//n=p*q
            System.out.println("2、计算的n是："+n);
            //step2.2:计算sn
            BigInteger sn=(p.subtract(new BigInteger("1"))).multiply(q.subtract(new BigInteger("1")));//sn=(p-1)*(q-1)
            System.out.println("   计算的sn是："+sn);
            //step3:随机选取e
            BigInteger e=rsa.getE(sn);//0<e<sn && e和 sn互素
            System.out.println("3、选取的e是："+e);
            //step4:计算d
            BigInteger d=rsa.getD(sn, e).mod(sn);//d同时与n和sn互素
            System.out.println("4、计算的d是："+d);
            //step5:B将n和e作为公钥公开
            System.out.println("5、公钥：n="+n+" e="+e);
            //step6:用户A获取到公钥
            System.out.println("6、用户A已经获取到公钥了，可以开始发数据了...");
            //step7:进行加密
            System.out.println("7、请输入要发送的明文（仅数字）:");
            Scanner scanner=new Scanner(System.in);
            BigInteger m=new BigInteger(scanner.next());
            BigInteger c=rsa.getC(m,e,n);//计算密文
            System.out.println("加密后的密文是："+c);
            //step8:进行解密
            BigInteger mm=rsa.getDecrypt(c,n,d);//进行解密
            System.out.println("8、解密后的的结果是："+mm);
        }
        /**
         * 随机产生两个大素数: res[0]、res[1]
         * @return
         */
        public BigInteger[] getPrimeNumber(){
            BigInteger p=null;
            BigInteger q=null;
            BigInteger[] res=new BigInteger[2];
            Random random = new Random();
            p=BigInteger.probablePrime(64, random);//为了效率，此处设为64
            q=BigInteger.probablePrime(64, random);
            res[0]=p;
            res[1]=q;
            return res;
        }
        /**
         * 随机选取e
         * 0<e<sn && e和 sn互素
         * @param sn
         * @return
         */
        public BigInteger getE(BigInteger sn){
            BigInteger e = null;
            //说明:此处把产生的e位数-2，是防止 nextProbablePrime()方法产生的素数大于sn
            int length = sn.toString().length()-2;// length为随机数位数
            e=new BigInteger(sn.toString().subSequence(0, length-2).toString()).nextProbablePrime();
            return e;
        }
        /**
         * 选取d
         * d同时与n和sn互素
         * @param n
         * @param sn
         * @return
         */
        public BigInteger getD(BigInteger sn,BigInteger e){//Euclid算法
            BigInteger[] ret = new BigInteger[3];
            BigInteger u = BigInteger.valueOf(1), u1 = BigInteger.valueOf(0);
            BigInteger v = BigInteger.valueOf(0), v1 = BigInteger.valueOf(1);
            if (e.compareTo(sn) > 0) {
                BigInteger tem = sn;
                sn = e;
                e = tem;
            }
            while (e.compareTo(BigInteger.valueOf(0)) != 0) {
                BigInteger tq = sn.divide(e); // tq = sn / e
                BigInteger tu = u;
                u = u1;
                u1 = tu.subtract(tq.multiply(u1)); // u1 =tu - tq * u1
                BigInteger tv = v;
                v = v1;
                v1 = tv.subtract(tq.multiply(v1)); // v1 = tv - tq * v1
                BigInteger tsn = sn;
                sn = e;
                e = tsn.subtract(tq.multiply(e)); // e = tsn - tq * e
                ret[0] = u;
                ret[1] = v;
                ret[2] = sn;
            }
            return ret[1];
        }
        /**
         * 计算密文
         * c=(m^e)%n
         * @param m 明文
         * @param e
         * @param n
         * @return
         */
        public BigInteger getC(BigInteger m,BigInteger e,BigInteger n){
            BigInteger c=null;
            c=m.modPow(e, n);//返回其值为 (m^e  mod n)
            return c;
        }
        /**
         * 计算解密
         * c^d=m%n m为解密后的结果
         * @param c 密文
         * @param n 公钥
         * @param d 私钥
         * @return
         */
        public BigInteger getDecrypt(BigInteger c,BigInteger n,BigInteger d){
            BigInteger m=null;
            m=c.modPow(d, n);//返回其值为 (c^d  mod n)
            return m;
        }


}
